package com.heima.article.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.heima.article.dto.ArticleStreamMessage;
import com.heima.article.entity.ApArticle;
import com.heima.article.service.IApArticleService;
import com.heima.article.service.IHotArticleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @Author 请不要叫我高司令
 * @Date 2022/4/21 19:40
 * @Version 1.0
 */

@Service
public class HotArticleServiceImpl implements IHotArticleService {


    @Autowired
    private IApArticleService apArticleService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public void compute() {

        LambdaQueryWrapper<ApArticle> queryWrapper = new LambdaQueryWrapper<>();
        //查询前5天的文章
        // 从当天的0点0分0秒往前推5天
        Date now = new Date();
        Date to = new Date(now.getYear(), now.getMonth(), now.getDate());
        Date from = new Date(to.getTime() - 5 * 24 * 3600 * 1000);
        queryWrapper.le(ApArticle::getPublishTime, to);
        queryWrapper.ge(ApArticle::getPublishTime, from);
        List<ApArticle> list = apArticleService.list(queryWrapper);

        //计算文章分值
        if (CollectionUtil.isNotEmpty(list)) {
            for (ApArticle article : list) {
                double score = computeScore(article);
                System.out.println(score);
                // 可以根据分值排序取数据  zset支持带分值排序的功能
                //首页的前缀
                String key = "first_page_article_0";
                //只能保存不变的数据
                ApArticle toCache = new ApArticle();
                toCache.setId(article.getId());
                toCache.setTitle(article.getTitle());
                toCache.setAuthorId(article.getAuthorId());
                toCache.setAuthorName(article.getAuthorName());
                toCache.setChannelId(article.getChannelId());
                toCache.setAuthorName(article.getChannelName());
                toCache.setLayout(article.getLayout());
                toCache.setImages(article.getImages());
                toCache.setCreatedTime(article.getCreatedTime());
                toCache.setPublishTime(article.getPublishTime());
                toCache.setStaticUrl(article.getStaticUrl());
                //对象转json字符串
                String value = JSON.toJSONString(toCache);
                //存到redis中
                redisTemplate.opsForZSet().add(key, value, score);
                //加上过期时间
                redisTemplate.expire(key, 23 * 60 + 59, TimeUnit.MINUTES);

                //为每个频道首页缓存数据
                String channelKey = "first_page_article_" + article.getChannelId();
                redisTemplate.opsForZSet().add(channelKey, value, score);
                //过期时间
                redisTemplate.expire(channelKey, 23 * 60 + 59, TimeUnit.MINUTES);

            }
        }
        System.out.println("热点数据保存成功。。。");

    }

    @Override
    public void update(ArticleStreamMessage message) {

        // 根据文章id查询文章
        ApArticle article = apArticleService.getById(message.getArticleId());

        // 计算文章的本次聚合的操作分值
        double scorePlus = computeScore(message);

        // 将文章不变的字段保存(页面需要的字段)
        ApArticle articleToCache = new ApArticle();
        articleToCache.setId(article.getId());
        articleToCache.setTitle(article.getTitle());
        articleToCache.setAuthorId(article.getAuthorId());
        articleToCache.setAuthorName(article.getAuthorName());
        articleToCache.setChannelId(article.getChannelId());
        articleToCache.setAuthorName(article.getChannelName());
        articleToCache.setLayout(article.getLayout());
        articleToCache.setImages(article.getImages());
        articleToCache.setCreatedTime(article.getCreatedTime());
        articleToCache.setPublishTime(article.getPublishTime());
        articleToCache.setStaticUrl(article.getStaticUrl());

        // 判断当前的文章数据是否已经缓存在redis中
        //hot_article_first_page_0这个包含五天内所有的文章
        String firstKey = "first_page_article_0";
        String channelKey = "first_page_article_" + article.getChannelId();
        String json = JSON.toJSONString(articleToCache);

        //根据key和vlaue去获取分数
        Double score = redisTemplate.opsForZSet().score(firstKey, json);
        if (score != null) {
            // 如果已经换成在redis中,在redis中加上增量的分值
            redisTemplate.opsForZSet().incrementScore(firstKey, json, scorePlus);
            redisTemplate.opsForZSet().incrementScore(channelKey, json, scorePlus);
        } else {
            // 如果不在redis中,需要先计算之前的分值,再加上增量的分值,再添加数据到redis中
            double scorePre = computeScore(article);
            double scoreFinal = scorePre + scorePlus;
            redisTemplate.opsForZSet().add(firstKey, json, scoreFinal);
            redisTemplate.opsForZSet().add(channelKey, json, scoreFinal);
        }

        //需要将本次聚合操作的数据写入到文章中
        LambdaUpdateWrapper<ApArticle> updateWrapper = new LambdaUpdateWrapper<>();
        // 在MySQL中,一条sql语句本身就是一个事务
        // update ap_article set views = views + 1,likes = likes + 1,comment = comment + 1,collection = collection + 1 where id = ?
        updateWrapper.eq(ApArticle::getId, message.getArticleId())
                .setSql("views = views + " + message.getView())
                .setSql("likes = likes + " + message.getLike())
                .setSql("comment = comment + " + message.getComment())
                .setSql("collection = collection + " + message.getCollect());
        apArticleService.update(updateWrapper);

    }

    /**
     * 计算增量分数
     *
     * @param message
     * @return
     */
    private double computeScore(ArticleStreamMessage message) {

        double score = 0;
        score += message.getView() * 1 * 3;
        score += message.getLike() * 3 * 3;
        score += message.getComment() * 5 * 3;
        score += message.getCollect() * 8 * 3;
        return score;

    }

    /**
     * 计算文章的分值
     *
     * @param article
     * @return
     */
    private double computeScore(ApArticle article) {

        double score = 0;

        if (article.getViews() != null) {
            score += 1 * article.getViews();
        }
        if (article.getLikes() != null) {
            score += 2 * article.getLikes();
        }
        if (article.getComment() != null) {
            score += 3 * article.getComment();
        }
        if (article.getCollection() != null) {
            score += 4 * article.getCollection();
        }
        return score;

    }
}
